/*
 * Tmt.m
 * --------
 * Class containing methods to interact with the Tmt web services. 
 *
 * Author: Chris Lee <clee@mg8.org>
 * Contributor: Michael Lewis <mikelikespie@gmail.com>
 * License: GPL v2 <http://www.opensource.org/licenses/gpl-license.php>
 */

#import <Foundation/Foundation.h>
#import <UIKit/UIApplication.h>
#import <UIKit/UIAlertSheet.h>
#import "SmsUtilsApplication.h"
#import "Tmt.h"
#import "Message.h"

#include <unistd.h>
#include <sys/xattr.h>

@class NSXMLNode, NSXMLElement, NSXMLDocument;

@implementation Tmt

- (id)initWithMessageView: (MessageView *)messageview
{
	if (![super init])
		return nil;

	[_messageview release];

	_messageview = [messageview retain];
	_settings = [NSUserDefaults standardUserDefaults];


	//Load defaults
	//
	NSBundle * bundle = [NSBundle mainBundle];
	NSString *ppath = [bundle pathForResource:@"Defaults" ofType:@"plist"];
	NSPropertyListFormat format;
	NSString *error;

	NSData *plistData = [NSData dataWithContentsOfFile:ppath];

	NSDictionary *dict = [NSPropertyListSerialization
		propertyListFromData:plistData
			mutabilityOption:NSPropertyListImmutable
					  format:&format
			errorDescription:&error];
	_default_strings = [[NSDictionary alloc] initWithDictionary:dict];
	NSLog(@"mowmow");

	NSLog(@"mowmow %@", _default_strings );

	return self;
}

- (void)dealloc
{
	[_messageview release];
	[super dealloc];
}


#pragma mark XML helper functions
- (BOOL)sanityCheck: (id)responseDocument error: (NSError *)err
{
	NSXMLNode *rsp = [[responseDocument children] objectAtIndex: 0];
	if (![[rsp name] isEqualToString: @"rsp"]) {
		NSLog(@"This is not an <rsp> tag, but a %@ tag! Bailing out.", [rsp XMLString]);
		return FALSE;
	}

	NSXMLElement* element = [[NSClassFromString(@"NSXMLElement") alloc] initWithXMLString: [rsp XMLString] error: &err];
	if (![[[element attributeForName: @"stat"] stringValue] isEqualToString: @"ok"]) {
		NSLog(@"The status is not 'ok', and we have no error recovery.");
		[element release];
		return FALSE;
	}

	[element release];
	return TRUE;
}

/*
 * Returns an array of XMLNode objects with name matching nodeName.
 */
- (NSArray *)getXMLNodesNamed: (NSString *)nodeName fromResponse: (NSData *)responseData
{
	NSError *err = nil;
	if( !responseData ) {
		[_messageview popupAlertSheetWithString:@"We're not getting responses from the server."];
		return nil;
	}
	NSXMLDocument *responseDoc = [[NSClassFromString(@"NSXMLDocument") alloc] initWithData: responseData options: 0 error: &err];
	if (![self sanityCheck: responseDoc error: err]) {
		NSLog(@"Tmt returned an error!");
		[_messageview popupAlertSheetWithString:@"We're having some issues."];
		[responseDoc release];
		return nil;
	}

	NSMutableArray *matchingNodes = [NSMutableArray array];
	NSArray *nodes = [responseDoc children];
	NSEnumerator *chain = [nodes objectEnumerator];
	NSXMLNode *node = nil;

	while ((node = [chain nextObject])) {
		if (![[node name] isEqualToString: nodeName]) {
			nodes = [[nodes lastObject] children];
			chain = [nodes objectEnumerator];
			continue;
		}

		[matchingNodes addObject: node];
	}

	[responseDoc release];

	return [NSArray arrayWithArray: matchingNodes];
}

/*
 * Returns a dictionary filled with the node names, node values, node attribute names, and attribute values.
 */
- (NSDictionary *)getXMLNodesAndAttributesFromResponse: (NSData *)responseData
{
	NSError *err = nil;
	if( !responseData ) {
		[_messageview popupAlertSheetWithString:@"We're not getting responses from the server."];
		return nil;
	}
	NSXMLDocument *responseDoc = [[NSClassFromString(@"NSXMLDocument") alloc] initWithData: responseData options: 0 error: &err];
	if (![self sanityCheck: responseDoc error: err]) {
		NSLog(@"Tmt returned an error!");
		[_messageview popupAlertSheetWithString:@"We're having some issues."];
		[responseDoc release];
		return nil;
	}

	NSMutableDictionary *nodesWithAttributes = [NSMutableDictionary dictionary];
	NSArray *nodes = [responseDoc children];
	NSEnumerator *chain = [nodes objectEnumerator];
	NSXMLNode *node = nil;

	while ((node = [chain nextObject])) {
		id element = [[NSClassFromString(@"NSXMLElement") alloc] initWithXMLString: [node XMLString] error: &err];
		if ([[element attributes] count] > 0) {
			NSEnumerator *attributeChain = [[element attributes] objectEnumerator];
			id attribute = nil;
			while ((attribute = [attributeChain nextObject]))
				[nodesWithAttributes setObject: [attribute stringValue] forKey: [NSString stringWithFormat: @"%@%@", [node name], [attribute name]]];
		}

		[nodesWithAttributes setObject: [node stringValue] forKey: [node name]];

		if ([[node children] count] > 0 && [[[[node children] objectAtIndex: 0] name] length] > 0) {
			nodes = [node children];
			chain = [nodes objectEnumerator];
		}

		[element release];
	}

	[responseDoc release];

	return [NSDictionary dictionaryWithDictionary: nodesWithAttributes];
}

#pragma mark internal functions
/*
 * Returns a URL with the parameters and values properly appended, including the call signing that Tmt requires from our app.
 *
 * This method made possible by extending system classes (without having to inherit from them.) Hooray!
 */
- (NSURL *)signedURL: (NSDictionary *)parameters withBase: (NSString *)base
{
	NSMutableString *url = [NSMutableString stringWithFormat: @"%@?", base];
	NSMutableString *sig = [NSMutableString stringWithString: SMSUTILS_SHARED_SECRET];

	[sig appendString: [[parameters pairsJoinedByString: @"" encoded: NO] componentsJoinedByString: @""]];
	[url appendString: [[parameters pairsJoinedByString: @"=" encoded: YES] componentsJoinedByString: @"&"]];
	[url appendString: [NSString stringWithFormat: @"&api_sig=%@", [sig md5HexHash]]];

	return [NSURL URLWithString: url];
}

/*
 * By default, we want the TMT_REST_URL as the base for our calls.
 */
- (NSURL *)signedURL: (NSDictionary *)parameters
{
	return [self signedURL: parameters
				  withBase: [_default_strings objectForKey:@"tmt_rest_url"]];
}

/*
 * Returns a one-time-use authorization URL; this URL is a page where the user can tell Tmt to give us permission to upload pictures to their account.
 */
- (NSURL *)authURL {
	NSLog(@"Making auth url");
	NSArray *keys = [NSArray arrayWithObjects: @"api_key", @"perms", @"frob", nil];
	NSArray *vals = [NSArray 
		arrayWithObjects: SMSUTILS_API_KEY, TMT_WRITE_PERMS, [self frob], nil];

	NSDictionary *params = [NSDictionary dictionaryWithObjects: vals
													   forKeys: keys];

	NSLog(@"Done making auth url");
   return [self signedURL: params withBase: [_default_strings objectForKey:@"tmt_auth_url"]];
}

/*
 * Get a frob from Tmt, to put in the URL that we send the user to to get their permission to upload pics.
 */
- (NSString *)frob
{
	NSLog(@"StartFrob");
	NSArray *keys = [NSArray arrayWithObjects: @"api_key", @"method", nil];
	NSLog(@"f %@",[_default_strings objectForKey:@"tmt_get_frob"] );
	
	NSArray *vals = [NSArray arrayWithObjects: SMSUTILS_API_KEY,
			   [_default_strings objectForKey:@"tmt_get_frob"], nil];
	NSLog(@"f");
	NSDictionary *params = [NSDictionary dictionaryWithObjects: vals forKeys: keys];

	NSURL *url = [self signedURL: params];
	NSLog(@"f");
	NSData *responseData = [NSData dataWithContentsOfURL: url];
	NSLog(@"f");
	NSLog( [NSString stringWithCString:[responseData bytes] length:[responseData length]] );
	NSLog(@"f");

	NSString *_frob = [[[self getXMLNodesNamed: @"frob" fromResponse: responseData] lastObject] stringValue];
	NSLog(@"f");

	[_settings setObject: _frob forKey: @"frob"];
	[_settings synchronize];

	NSLog(@"EndFrob");
	return [NSString stringWithString: _frob];
}

#pragma mark externally-visible interface

/*
 * Pop up a dialog so the user can tell Tmt it's cool for us to push pictures to their account.
 */
- (BOOL)sendToGrantPermission
{
	NSLog(@"!!!!!!");
	UIAlertSheet *alertSheet = [[UIAlertSheet alloc] initWithFrame: CGRectMake(0.0f, 0.0f, 320.0f, 240.0f)];
	[alertSheet setTitle: @"Can't upload to Tmt"];
	[alertSheet setBodyText: @"Treasuremytext needs your permission to archive SMS to your account"];
	[alertSheet addButtonWithTitle: @"Proceed"];
	[alertSheet addButtonWithTitle: @"Cancel"];
	[alertSheet setDelegate: self];
	[alertSheet popupAlertAnimated: YES];
	[_settings setBool: TRUE forKey: @"sentToGetToken"];
	return TRUE;
}

/*
 * We have a frob that Tmt generated, and we used it in the URL we sent the user to (so that they could give us permission to upload pictures to their account). Now, we assume the user clicked on the 'Okay!' button the page we sent them to go click, and our frob can now be traded for a token.
 */
- (BOOL)tradeFrobForToken
{
	NSArray *keys = [NSArray arrayWithObjects: @"api_key", @"method", @"frob", nil];
	NSArray *vals = [NSArray arrayWithObjects: SMSUTILS_API_KEY, [_default_strings objectForKey:@"tmt_get_token"], [_settings stringForKey: @"frob"], nil];
	NSDictionary *params = [NSDictionary dictionaryWithObjects: vals forKeys: keys];

	NSData *responseData = [NSData dataWithContentsOfURL: [self signedURL: params]];
	NSLog( [NSString stringWithCString:[responseData bytes] length:[responseData length]] );

	NSDictionary *tokenDictionary = [self getXMLNodesAndAttributesFromResponse: responseData];
	NSArray *responseKeys = [tokenDictionary allKeys];
	if (!([responseKeys containsObject: @"token"] && [responseKeys containsObject: @"usernsid"] && [responseKeys containsObject: @"userusername"])) {
		NSLog(@"Tmt returned an error!");
		[_settings removeObjectForKey: @"frob"];
		[_settings synchronize];
		[self sendToGrantPermission];
		return FALSE;
	}

	[_settings setObject: [tokenDictionary objectForKey: @"token"] forKey: @"token"];
	[_settings setObject: [tokenDictionary objectForKey: @"usernsid"] forKey: @"nsid"];
	[_settings setObject: [tokenDictionary objectForKey: @"userusername"] forKey: @"username"];
	[_settings removeObjectForKey: @"frob"];
	[_settings synchronize];
	return TRUE;
}

/*
 * We have a token, but is it valid? Maybe the user decided to de-authorize us and we can't push photos to their account anymore. This is how we make sure our token is valid.
 */
- (BOOL)postMessageWithMessage:(Message*)message
{
	NSArray *keys = [NSArray arrayWithObjects: @"api_key", @"auth_token", @"method", @"text", @"address", @"name", @"date", @"flags", nil];

	NSArray *vals = [NSArray arrayWithObjects: SMSUTILS_API_KEY,
					  [_settings stringForKey: @"token"],
			   [_default_strings objectForKey: @"tmt_add_sms"],
					  [message text],
					  [message address],
					  [message fullName],
					  [NSString stringWithFormat:@"%d", [message date]],
					  [NSString stringWithFormat:@"%d", [message flags]],
					  nil];
	NSDictionary *params = [NSDictionary dictionaryWithObjects: vals forKeys: keys];
	NSLog(@"URL %x %@", params, [[self signedURL: params] absoluteString]);
	NSData *responseData = [NSData dataWithContentsOfURL: [self signedURL: params]];
	NSDictionary *tokenDictionary = [self getXMLNodesAndAttributesFromResponse: responseData];
	if( !tokenDictionary ) {
		return FALSE;
	}
	NSArray *responseKeys = [tokenDictionary allKeys];

	return TRUE;
}
/*
 * We have a token, but is it valid? Maybe the user decided to de-authorize us and we can't push photos to their account anymore. This is how we make sure our token is valid.
 */
- (BOOL)checkToken
{
	NSArray *keys = [NSArray arrayWithObjects: @"api_key", @"auth_token", @"method", nil];
	NSLog(@"strings %@", _default_strings);
	NSArray *vals = [NSArray arrayWithObjects: SMSUTILS_API_KEY, [_settings stringForKey: @"token"], [_default_strings objectForKey:@"tmt_check_token"], nil];
	NSLog(@"Token: %@", [_default_strings objectForKey:@"tmt_check_token"]);
	NSDictionary *params = [NSDictionary dictionaryWithObjects: vals forKeys: keys];
	NSLog(@"URL %@", [[self signedURL: params] absoluteString]);
	NSData *responseData = [NSData dataWithContentsOfURL: [self signedURL: params]];
	NSDictionary *tokenDictionary = [self getXMLNodesAndAttributesFromResponse: responseData];
	if( !tokenDictionary ) {
		NSLog(@"Issue with token dictionary");
		return FALSE;
	}
	NSArray *responseKeys = [tokenDictionary allKeys];
	if (!([responseKeys containsObject: @"token"] && [responseKeys containsObject: @"usernsid"] 
	   && [responseKeys containsObject: @"userusername"])) {
		NSLog(@"Failed the sanity check when verifying our token. Bailing!");
		[_settings setBool: FALSE forKey: @"sentToGetToken"];
		[self sendToGrantPermission];
		return FALSE;
	}

	NSLog(@"Well, our token seems good.");
	return TRUE;
}

- (void)alertSheet: (UIAlertSheet *)sheet buttonClicked: (int)button
{
    [sheet dismiss];
	[sheet release];

	switch (button) {
		case 1:
			NSLog(@"Stuff %x %x", mainApp, [self authURL]);
			NSLog(@"Trying to open URL with mainapp %x at %@", mainApp, [self authURL]);
			[mainApp openURL: [self authURL]];
			break;
		default:
//[_messageview terminate];
			break;
	}
}
@end
